package com.cet.designapplication.service;

import com.cet.designapplication.composite.AbstractProductItem;
import com.cet.designapplication.composite.ProductComposite;
import com.cet.designapplication.pojo.ProductItem;
import com.cet.designapplication.repo.ProductItemRepository;
import com.cet.designapplication.utils.RedisCommonProcessor;
import com.cet.designapplication.visitor.AddItemVisitor;
import com.cet.designapplication.visitor.DelItemVisitor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author NSOC
 */

@Service
public class ProductItemService {

    @Autowired
    private RedisCommonProcessor redisCommonProcessor;

    @Autowired
    private ProductItemRepository productItemRepository;

    @Autowired
    private AddItemVisitor addItemVisitor;

    @Autowired
    private DelItemVisitor delItemVisitor;

    public ProductComposite fetchAllItems() {

        Object cacheItems = redisCommonProcessor.get("items");
        if (cacheItems != null) {
            return (ProductComposite) cacheItems;
        }

        List<ProductItem> fetchDbItems = productItemRepository.findAll();
        ProductComposite items = generateProductTree(fetchDbItems);
        if (items == null) {
            throw new UnsupportedOperationException("Product items should not be empty in DB!");
        }
        redisCommonProcessor.set("items", items);
        return items;
    }

    private ProductComposite generateProductTree(List<ProductItem> fetchDbItems) {
        List<ProductComposite> composites = new ArrayList<>(fetchDbItems.size());
        fetchDbItems.forEach(dbItem -> composites.add(ProductComposite.builder()
                .id(dbItem.getId())
                .name(dbItem.getName())
                .pid(dbItem.getPid())
                .build()));
        Map<Integer, List<ProductComposite>> groupingList =
                composites.stream().collect(Collectors.groupingBy(ProductComposite::getPid));

        composites.forEach(item -> {
            List<ProductComposite> list = groupingList.get(item.getId());
            item.setChild(list == null ? new ArrayList<>() : list.stream()
                    .map(x -> (AbstractProductItem) x).collect(Collectors.toList()));
        });
        return composites.size() == 0 ? null : composites.get(0);
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public ProductComposite addItem(ProductItem item) {
        //先更新数据库
        productItemRepository.addItem(item.getName(), item.getPid());
        ProductComposite addItem = ProductComposite.builder()
                .id(productItemRepository.findByNameAndPid(item.getName(), item.getPid()).getId())
                .name(item.getName())
                .pid(item.getPid())
                .child(new ArrayList<>())
                .build();

        AbstractProductItem updatedItems = addItemVisitor.visit(addItem);

        //后更新缓存
        redisCommonProcessor.set("items", updatedItems);
        return (ProductComposite) updatedItems;
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public ProductComposite delItem(ProductItem item) {
        //先更新数据库
        productItemRepository.delItem(item.getId());

        ProductComposite delItem = ProductComposite.builder()
                .id(item.getId())
                .name(item.getName())
                .pid(item.getPid())
                .build();
        AbstractProductItem updatedItems = delItemVisitor.visit(delItem);
        //后更新缓存
        redisCommonProcessor.set("items", updatedItems);
        return (ProductComposite) updatedItems;
    }

}
